package service

import (
	"bufio"
	"context"
	"encoding/json"
	"fmt"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/network"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"
	"github.com/gorilla/websocket"
	"github.com/toolkits/pkg/logger"
	"log"
)

// 查询指定容器
func GetContainer(cli *client.Client, containerId string) (containerInfo types.ContainerJSON, err error) {
	ctx := context.Background()
	containerJson, err := cli.ContainerInspect(ctx, containerId)
	if err != nil {
		fmt.Println(err)
		return containerJson, err
	}
	return containerJson, nil
}

// GetContainers
// 获取容器列表
func GetContainers(dockerCli *client.Client) (containers []types.Container, err error) {
	//All-true相当于docker ps -a
	containers, err = dockerCli.ContainerList(context.Background(), types.ContainerListOptions{All: true})
	if err != nil {
		logger.Error(err)
		return containers, err
	}

	for _, container := range containers {
		logger.Info("%s %s\n", container.ID[:10], container.Image)
	}
	return containers, nil
}

//获取容器日志

func GetContainerLogs(dockerCli *client.Client, conn *websocket.Conn, containerId string) error {
	ctx := context.Background()
	logs, err := dockerCli.ContainerLogs(ctx, containerId, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true, Tail: "300"})
	if err != nil {
		fmt.Println(err)
		return err
	}

	logger.Infof("Begin input logs")

	scanner := bufio.NewScanner(logs)
	for scanner.Scan() {

		//检查websocket
		if err = conn.WriteMessage(websocket.TextMessage, []byte("OK")); err != nil {
			logger.Error(err)
			return err
		}

		text := scanner.Text()
		b, _ := json.Marshal(text)
		err = conn.WriteMessage(websocket.TextMessage, b)
		if err != nil {
			logger.Infof("写入错误", err)
			continue
		}
	}
	fmt.Println("End input logs")
	return nil
}

func ExecAttach(dockerCli *client.Client, execHr *types.HijackedResponse, containerId string) error {
	ctx := context.Background()
	// 在指定容器中执行/bin/bash命令
	ir, err := dockerCli.ContainerExecCreate(ctx, containerId, types.ExecConfig{
		AttachStdin:  true,
		AttachStdout: true,
		AttachStderr: true,
		Cmd:          []string{"/bin/sh"},
		Tty:          true,
	})
	if err != nil {
		return err
	}

	// 保持链接
	*execHr, err = dockerCli.ContainerExecAttach(ctx, ir.ID, types.ExecStartCheck{Detach: false, Tty: true})
	if err != nil {
		return err
	}
	return nil
}

func ExecContainer(execHr *types.HijackedResponse, conn *websocket.Conn) (err error) {

	//关闭I/O
	defer execHr.Close()

	// 输入命令
	_, err = execHr.Conn.Write([]byte("ls\r"))
	if err != nil {
		return err
	}

	// 输出
	scanner := bufio.NewScanner(execHr.Conn)
	for scanner.Scan() {
		fmt.Println(scanner.Text())
		text := scanner.Text()
		b, _ := json.Marshal(text)
		err = conn.WriteMessage(websocket.TextMessage, b)
		if err != nil {
			log.Println("写入错误", err)
			//return err
			continue
		}
	}
	logger.Info("容器输出结束")

	return nil
}

func SendCommend(cmdString string, execHr *types.HijackedResponse) (err error) {

	fmt.Println(cmdString)
	_, err = execHr.Conn.Write([]byte(cmdString + "\r"))
	if err != nil {
		return
	}
	return nil
}

// CreateContainer
// 创建容器
func CreateContainer(cli *client.Client, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (containerId string, err error) {
	ctx := context.Background()
	//创建容器
	resp, err := cli.ContainerCreate(ctx, config, hostConfig, networkingConfig, nil, containerName)
	if err != nil {
		fmt.Println(err.Error())
	}
	return resp.ID, nil
}

func CreateContainerRequest2DockerUse(createContainerRequest CreateContainerRequest) (config *container.Config, hostConfig *container.HostConfig) {

	//解释端口映射的map
	natPortSet := make(nat.PortSet)
	natPortMap := make(nat.PortMap)

	for portOut, portIn := range createContainerRequest.PortMap {
		key := nat.Port(fmt.Sprintf("%s", portOut))
		natPortSet[key] = struct{}{}
		natPortMap[key] = []nat.PortBinding{
			{
				HostIP:   "0.0.0.0",
				HostPort: portIn,
			},
		}
	}
	//解释挂载文件
	var volumeBinds []string
	for volumeOut, volumeIn := range createContainerRequest.VolumeMap {
		volumeBind := volumeOut + ":" + volumeIn
		volumeBinds = append(volumeBinds, volumeBind)
	}

	//组装结果
	config = &container.Config{
		Image:        createContainerRequest.ImagerName,
		Tty:          true,
		ExposedPorts: natPortSet,
	}

	hostConfig = &container.HostConfig{
		PortBindings: natPortMap,
		Binds:        volumeBinds,
	}
	return config, hostConfig
}

func StartContainer(cli *client.Client, containerId string) error {
	ctx := context.Background()
	err := cli.ContainerStart(ctx, containerId, types.ContainerStartOptions{})
	if err != nil {
		fmt.Println(err)
		return err
	}
	return nil
}

func StopContainer(cli *client.Client, containerId string) error {
	ctx := context.Background()
	err := cli.ContainerStop(ctx, containerId, container.StopOptions{})
	if err != nil {
		fmt.Println(err)
		return err
	}
	return nil
}

func RestartContainer(cli *client.Client, containerId string) error {
	ctx := context.Background()
	err := cli.ContainerRestart(ctx, containerId, container.StopOptions{})
	if err != nil {
		fmt.Println(err)
		return err
	}
	return nil
}

func DeleteContainer(cli *client.Client, containerId string) error {
	ctx := context.Background()
	err := cli.ContainerRemove(ctx, containerId, types.ContainerRemoveOptions{})
	if err != nil {
		fmt.Println(err)
		return err
	}
	return nil
}
